import {WidgetFactory} from "@/modules/IDE/widgetFactory";
import {widgetHashKeys} from '@/modules/IDE/widget/atom/widget';
import {generateUID} from "@/utils";
import {Database} from "@/modules/IDE/DBHandler";
import moment from "moment";

export class UndoRedoHandler {
  constructor(limit = 50) {
    // 撤销/重做的最大步数限制
    this.limit = limit;
    // 操作记录数组，只记录 operationId，具体的组件数据在 database 中关联查询
    this.operationArr = [];
    // 指向当前激活的操作记录索引
    this.current = -1;
    // 标志撤销/重做是否已到达头/尾，避免到达数组头或尾时重复操作
    this.reachHead = false;
    this.reachTail = false;
    // 初始化 IndexDB 数据库
    this.db = new Database();
    // 初始化先清空上一次浏览器的缓存操作记录数据
    this.clearAllOperationData().then();
  }

  // 当发生用户操作，同步记录至 operationArr 和 database
  async operationHdl(operations = []) {
    // console.log('##### operation trigger #####');
    // 如果 current 指针没有指向 operationArr 的最后，表示正在执行撤销/重做
    // 此时如果产生了新的操作，则需要将 current 指针之后的操作记录剔除
    // 如果已经撤销到头部，并且此时产生新的操作，则清空所有之前操作记录记录
    if (this.reachHead) {
      await this.db.deleteAllOperation();
      this.operationArr = [];
    }
    if (this.current < this.operationArr.length - 1) {
      const deleteCount = this.operationArr.length - this.current - 1;
      for (let i = 1; i <= deleteCount; i++) {
        const {operationId} = this.operationArr[this.current + i];
        await this.db.deleteOperation(operationId);
      }
      this.operationArr.splice(this.current + 1);
    }
    // 只要产生了新的操作，则重置头/尾标志
    this.reachHead = false;
    this.reachTail = false;
    await this.saveOperationToDB(operations);
  }

  // 将操作记录至本地数据库
  async saveOperationToDB(operations = []) {
    // 生成唯一 operationId 随机数
    const operationId = generateUID();
    const timestamp = moment().format('x');
    this.operationArr.push({operationId, timestamp});
    // 记录一个操作产生的所有变更
    const changes = [];
    operations.forEach(operation => {
      const {operationType, widgets = []} = operation;
      switch (operationType) {
        case 'add':
        case 'delete':
          widgets.forEach(widget => {
            changes.push({
              operationType: operationType,
              widget: WidgetFactory.getWidgetNecessaryProperty(widget),
            });
          });
          break;
        case 'edit':
          widgets.forEach(widget => {
            const {from = {}, to = {}} = widget;
            changes.push({
              operationType: operationType,
              from: WidgetFactory.getWidgetNecessaryProperty(from),
              to: WidgetFactory.getWidgetNecessaryProperty(to)
            });
          });
          break;
        default:
          break;
      }
    });
    await this.db.addOperation({
      operationId: operationId,
      changes: changes,
      timestamp: timestamp,
    });
    // 如果超出步数限制，则将旧的操作记录删除
    await this.limitHdl();
    // current 指针指向操作记录数组最后一个元素
    this.current = this.operationArr.length - 1;
  }

  // 超出最大步数限制，则将最先进入操作记录数组的数据剔除
  async limitHdl() {
    if (this.operationArr.length > this.limit) {
      const {operationId} = this.operationArr[0];
      this.operationArr.splice(0, 1);
      await this.db.deleteOperation(operationId);
    }
  }

  async undo() {
    if (!this.operationArr.length) return [];
    if (this.reachHead) return [];
    // 对于撤销，先取出当前的操作记录数据进行撤销，再将指针前移
    const optWidgets = await this.execute('undo');
    this.reachHead = this.current === 0;
    this.reachTail = false;
    this.current = Math.max(0, this.current - 1);
    // 返回当前正在做重做操作的组件集合
    return optWidgets;
  }

  async redo() {
    if (!this.operationArr.length) return [];
    if (this.reachTail) return [];
    // 对于重做，先指针后移，再取出当前的操作记录数据进行重做
    if (this.reachHead) {
      this.current = 0;
    } else {
      this.current = Math.min(this.operationArr.length - 1, this.current + 1);
    }
    const optWidgets = await this.execute('redo');
    this.reachTail = this.current === this.operationArr.length - 1;
    this.reachHead = false;
    // 返回当前正在做重做操作的组件集合
    return optWidgets;
  }

  async execute(action) {
    // 根据 operationId 在 database 中查找具体的组件变更内容
    const {operationId} = this.operationArr[this.current];
    const {changes = []} = await this.db.getOperation(operationId) || {};
    // 记录撤销/重做动作被更改的组件集合
    const optWidgets = [];
    // 使用操作记录数据给组件实例赋值
    switch (action) {
      case 'undo':
        changes.forEach(change => {
          const {operationType, widget: widgetCfg = {}, from = {}} = change;
          switch (operationType) {
            case 'add':
            case 'delete': {
              const widget = WidgetFactory.getWidgetById(widgetCfg.id);
              widget.isDeleted = operationType === 'add';
              optWidgets.push(widget);
              break;
            }
            case 'edit': {
              const widget = WidgetFactory.getWidgetById(from.id);
              widgetHashKeys.forEach(key => widget[key] = from[key]);
              optWidgets.push(widget);
              break;
            }
            default:
              break;
          }
        });
        break;
      case 'redo':
        changes.forEach(change => {
          const {operationType, widget: widgetCfg = {}, to = {}} = change;
          switch (operationType) {
            case 'add':
            case 'delete': {
              const widget = WidgetFactory.getWidgetById(widgetCfg.id);
              widget.isDeleted = operationType === 'delete';
              optWidgets.push(widget);
              break;
            }
            case 'edit': {
              const widget = WidgetFactory.getWidgetById(to.id);
              widgetHashKeys.forEach(key => widget[key] = to[key]);
              optWidgets.push(widget);
              break;
            }
            default:
              break;
          }
        });
        break;
      default:
        break;
    }
    return this.getSingleOperatedWidgets(optWidgets);
  }

  // 查询某个子级的父级是否被选中，如果子级的父级也被选中，则将该子级去除
  getSingleOperatedWidgets(optWidgets = []) {
    const singleWidgets = [];
    const widgetIds = _.map(optWidgets, 'id') || [];
    const judgeParentExist = (widget) => {
      let parentExist = false;
      if (!widget.parentId) return false;
      if (widgetIds.includes(widget.parentId)) {
        parentExist = true;
      } else {
        const parent = WidgetFactory.getWidgetById(widget.parentId);
        parentExist = judgeParentExist(parent);
      }
      return parentExist;
    }
    const subPageIds = [];
    optWidgets.forEach(widget => {
      const {relateSubPageId} = widget;
      if (!relateSubPageId) return;
      if (subPageIds.includes(relateSubPageId)) return;
      subPageIds.push(relateSubPageId);
    });
    optWidgets.forEach(widget => {
      if (subPageIds.includes(widget.subPageId)) return;
      if (judgeParentExist(widget)) return;
      if (widget.isRoot && singleWidgets.find(sw => sw.isRoot)) return;
      singleWidgets.push(widget);
    });
    return singleWidgets;
  }

  // 清空所有操作记录数据
  async clearAllOperationData() {
    this.operationArr = [];
    this.current = -1;
    await this.db.deleteAllOperation();
  }
}
